﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Coolzon.UI.Core
{
    /// <summary>
    /// 用于Frame中显示特定的一个区域，定位到改区域的中心进行显示
    /// </summary>
    public class Locator
    {
        public IComponent Target { get; private set; }
        public Frame Frame { get; private set; }
        public Scene Scene { get; private set; }

        protected DoubleAnimation TTFAnimationX;
        protected DoubleAnimation TTFAnimationY;
        protected DoubleAnimation STFAnimationX;
        protected DoubleAnimation STFAnimationY;


        private Stretch _stretch;
        public Stretch Stretch
        {
            get
            {
                return _stretch;
            }
            set
            {
                if (_stretch != value)
                {
                    _stretch = value;
                    Relocate();
                }
            }
        }
        public Locator(Frame frame)
        {
            Frame = frame;
            Stretch = System.Windows.Media.Stretch.None;

            TTFAnimationX = new DoubleAnimation();
            TTFAnimationY = new DoubleAnimation();
            STFAnimationX = new DoubleAnimation();
            STFAnimationY = new DoubleAnimation();
            TTFAnimationX.Duration = new Duration(TimeSpan.FromSeconds(0.4));
            TTFAnimationY.Duration = new Duration(TimeSpan.FromSeconds(0.4));
            STFAnimationX.Duration = new Duration(TimeSpan.FromSeconds(0.4));
            STFAnimationY.Duration = new Duration(TimeSpan.FromSeconds(0.4));
            //TTFAnimationX.EasingFunction = new PowerEase();
            //TTFAnimationY.EasingFunction = new PowerEase();
            //STFAnimationX.EasingFunction = new PowerEase();
            //STFAnimationY.EasingFunction = new PowerEase();
            TTFAnimationX.Name = "TTFAnimationX";
            TTFAnimationX.Completed += TTFAnimationX_Completed;
        }

        void TTFAnimationX_Completed(object sender, EventArgs e)
        {
            AnimationClock clock = ((AnimationClock)sender);//.Timeline as DoubleAnimation;
            DoubleAnimation doubleAnimation = clock.Timeline as DoubleAnimation;
            if (clock.CurrentState == ClockState.Filling && string.Equals(doubleAnimation.Name, Target.XFullName) && Target != null)
            {
                ActiveTarget();
            }
        }

        private void ActiveTarget()
        {
            //async invoke active 13502338529
            Action<IComponent> action = new Action<IComponent>((p) =>
            {
                System.Threading.Thread.Sleep(20);
                Frame.Dispatcher.BeginInvoke(new Action<IComponent>((p1) =>
                {
                    Debug.WriteLine(string.Format("active:{0}", p1.XFullName));
                    p1.Active(Frame.PartContext);
                }), p);
            });
            action.BeginInvoke(Target, (p) =>
            {
            }, null);
        }

        public void Locate(IComponent target)
        {
            if (target == null)
                return;

            if (Target != target && Target != null)
            {
                PropertyValueKeeper.Instance.Restore();
                Target.Deactive();
            }
            Target = target;

            TTFAnimationX.Name = target.XFullName;
            if (Stretch == Stretch.Fill)
            {
                Locate4Fill(Frame, Frame.CurrentScene, (FrameworkElement)target);
            }
            else if (Stretch == Stretch.UniformToFill)
            {
                Locate4UniformToFill(Frame, Frame.CurrentScene, (FrameworkElement)target);
            }
            else if (Stretch == Stretch.Uniform)
            {
                Locate4Uniform(Frame, Frame.CurrentScene, (FrameworkElement)target);
            }
            else
            {
                Locate4None(Frame, Frame.CurrentScene, (FrameworkElement)target);
            }
        }
        public void Relocate()
        {
            if (Target == null)
                return;
            PropertyValueKeeper.Instance.Restore();
            Locate(Target);
        }

        private void Locate4None(Frame frame, Scene scene, FrameworkElement area)
        {
            // 等比拉伸
            if (scene != null && frame.ActualWidth > 0 && frame.ActualHeight > 0)
            {
                Point areaCenterPointInScene = area.TranslatePoint(new Point(0, 0), scene);
                double anchorX = -(areaCenterPointInScene.X - (Frame.ActualWidth - area.Width) / 2);
                double anchorY = -(areaCenterPointInScene.Y - (Frame.ActualHeight - area.Height) / 2);
                if (scene.RenderTransform == null || scene.RenderTransform == MatrixTransform.Identity)
                {
                    // create translate transform
                    TranslateTransform ttf = new TranslateTransform();
                    ttf.X = anchorX;
                    ttf.Y = anchorY;
                    // craete scale transform
                    ScaleTransform stf = new ScaleTransform();
                    stf.ScaleX = 1.0f;
                    stf.ScaleY = 1.0f;

                    TransformGroup tfGroup = new TransformGroup();
                    tfGroup.Children.Add(ttf);
                    tfGroup.Children.Add(stf);

                    scene.RenderTransform = tfGroup;
                    ActiveTarget();
                }
                else
                {
                    TransformGroup tfGroup = scene.RenderTransform as TransformGroup;
                    // get translate transform
                    TranslateTransform ttf = tfGroup.Children[0] as TranslateTransform;
                    // get scale transform
                    ScaleTransform stf = tfGroup.Children[1] as ScaleTransform;

                    TTFAnimationX.From = ttf.X;
                    TTFAnimationY.From = ttf.Y;
                    STFAnimationX.From = stf.ScaleX;
                    STFAnimationY.From = stf.ScaleY;

                    TTFAnimationX.To = anchorX;
                    TTFAnimationY.To = anchorY;
                    STFAnimationX.To = 1.0f;
                    STFAnimationY.To = 1.0f;

                    ttf.BeginAnimation(TranslateTransform.XProperty, TTFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    ttf.BeginAnimation(TranslateTransform.YProperty, TTFAnimationY, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleXProperty, STFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleYProperty, STFAnimationY, HandoffBehavior.SnapshotAndReplace);
                }
            }
        }

        private void Locate4Fill(Frame frame, Scene scene, FrameworkElement area)
        {
            // 拉伸至全屏
            if (scene != null && frame.ActualWidth > 0 && frame.ActualHeight > 0)
            {
                Point areaCenterPointInScene = area.TranslatePoint(new Point(0, 0), scene);
                double scaleX = frame.ActualWidth / area.Width;
                double scaleY = frame.ActualHeight / area.Height;

                if (scene.RenderTransform == null || scene.RenderTransform == MatrixTransform.Identity)
                {
                    // create translate transform
                    TranslateTransform ttf = new TranslateTransform();
                    ttf.X = -areaCenterPointInScene.X;
                    ttf.Y = -areaCenterPointInScene.Y;
                    // craete scale transform
                    ScaleTransform stf = new ScaleTransform();
                    stf.ScaleX = scaleX;
                    stf.ScaleY = scaleY;

                    TransformGroup tfGroup = new TransformGroup();
                    tfGroup.Children.Add(ttf);
                    tfGroup.Children.Add(stf);

                    scene.RenderTransform = tfGroup;
                    ActiveTarget();
                }
                else
                {
                    TransformGroup tfGroup = scene.RenderTransform as TransformGroup;
                    // get translate transform
                    TranslateTransform ttf = tfGroup.Children[0] as TranslateTransform;
                    // get scale transform
                    ScaleTransform stf = tfGroup.Children[1] as ScaleTransform;

                    TTFAnimationX.From = ttf.X;
                    TTFAnimationY.From = ttf.Y;
                    STFAnimationX.From = stf.ScaleX;
                    STFAnimationY.From = stf.ScaleY;

                    TTFAnimationX.To = -areaCenterPointInScene.X;
                    TTFAnimationY.To = -areaCenterPointInScene.Y;
                    STFAnimationX.To = scaleX;
                    STFAnimationY.To = scaleY;

                    ttf.BeginAnimation(TranslateTransform.XProperty, TTFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    ttf.BeginAnimation(TranslateTransform.YProperty, TTFAnimationY, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleXProperty, STFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleYProperty, STFAnimationY, HandoffBehavior.SnapshotAndReplace);
                }
            }
        }

        private void Locate4UniformToFill(Frame frame, Scene scene, FrameworkElement area)
        {
            // 等比拉伸,裁剪，没有黑边
            if (scene != null && frame.ActualWidth > 0 && frame.ActualHeight > 0)
            {
                //Point areaCenterPointInScene = area.TranslatePoint(new Point(area.Width / 2, area.Height / 2), scene);
                double scaleX = frame.ActualWidth / area.Width;
                double scaleY = frame.ActualHeight / area.Height;
                // 取最小的缩放值
                if (scaleX > scaleY)
                {
                    scaleY = scaleX;
                }
                else
                {
                    scaleX = scaleY;
                }

                Point areaCenterPointInScene = area.TranslatePoint(new Point(0, 0), scene);
                double anchorX = -(areaCenterPointInScene.X - (Frame.ActualWidth / scaleX - area.Width) / 2);
                double anchorY = -(areaCenterPointInScene.Y - (Frame.ActualHeight / scaleY - area.Height) / 2);
                if (scene.RenderTransform == null || scene.RenderTransform == MatrixTransform.Identity)
                {
                    // create translate transform
                    TranslateTransform ttf = new TranslateTransform();
                    ttf.X = anchorX;
                    ttf.Y = anchorY;
                    // craete scale transform
                    ScaleTransform stf = new ScaleTransform();
                    stf.ScaleX = scaleX;
                    stf.ScaleY = scaleY;

                    TransformGroup tfGroup = new TransformGroup();
                    tfGroup.Children.Add(ttf);
                    tfGroup.Children.Add(stf);

                    scene.RenderTransform = tfGroup;
                    ActiveTarget();
                }
                else
                {
                    TransformGroup tfGroup = scene.RenderTransform as TransformGroup;
                    // get translate transform
                    TranslateTransform ttf = tfGroup.Children[0] as TranslateTransform;
                    // get scale transform
                    ScaleTransform stf = tfGroup.Children[1] as ScaleTransform;

                    TTFAnimationX.From = ttf.X;
                    TTFAnimationY.From = ttf.Y;
                    STFAnimationX.From = stf.ScaleX;
                    STFAnimationY.From = stf.ScaleY;

                    TTFAnimationX.To = anchorX;
                    TTFAnimationY.To = anchorY;
                    STFAnimationX.To = scaleX;
                    STFAnimationY.To = scaleY;

                    ttf.BeginAnimation(TranslateTransform.XProperty, TTFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    ttf.BeginAnimation(TranslateTransform.YProperty, TTFAnimationY, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleXProperty, STFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleYProperty, STFAnimationY, HandoffBehavior.SnapshotAndReplace);
                }
            }
        }
        private void Locate4Uniform(Frame frame, Scene scene, FrameworkElement area)
        {
            // 等比拉伸,不裁剪，会有黑边
            if (scene != null && frame.ActualWidth > 0 && frame.ActualHeight > 0)
            {
                //Point areaCenterPointInScene = area.TranslatePoint(new Point(area.Width / 2, area.Height / 2), scene);
                double scaleX = frame.ActualWidth / area.Width;
                double scaleY = frame.ActualHeight / area.Height;
                // 取最小的缩放值
                if (scaleX < scaleY)
                {
                    scaleY = scaleX;
                }
                else
                {
                    scaleX = scaleY;
                }

                Point areaCenterPointInScene = area.TranslatePoint(new Point(0, 0), scene);
                double anchorX = -(areaCenterPointInScene.X - (Frame.ActualWidth / scaleX - area.Width) / 2);
                double anchorY = -(areaCenterPointInScene.Y - (Frame.ActualHeight / scaleY - area.Height) / 2);
                if (scene.RenderTransform == null || scene.RenderTransform == MatrixTransform.Identity)
                {
                    // create translate transform
                    TranslateTransform ttf = new TranslateTransform();
                    ttf.X = anchorX;
                    ttf.Y = anchorY;
                    // craete scale transform
                    ScaleTransform stf = new ScaleTransform();
                    stf.ScaleX = scaleX;
                    stf.ScaleY = scaleY;

                    TransformGroup tfGroup = new TransformGroup();
                    tfGroup.Children.Add(ttf);
                    tfGroup.Children.Add(stf);

                    scene.RenderTransform = tfGroup;
                    ActiveTarget();
                }
                else
                {
                    TransformGroup tfGroup = scene.RenderTransform as TransformGroup;
                    // get translate transform
                    TranslateTransform ttf = tfGroup.Children[0] as TranslateTransform;
                    // get scale transform
                    ScaleTransform stf = tfGroup.Children[1] as ScaleTransform;

                    TTFAnimationX.From = ttf.X;
                    TTFAnimationY.From = ttf.Y;
                    STFAnimationX.From = stf.ScaleX;
                    STFAnimationY.From = stf.ScaleY;

                    TTFAnimationX.To = anchorX;
                    TTFAnimationY.To = anchorY;
                    STFAnimationX.To = scaleX;
                    STFAnimationY.To = scaleY;

                    ttf.BeginAnimation(TranslateTransform.XProperty, TTFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    ttf.BeginAnimation(TranslateTransform.YProperty, TTFAnimationY, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleXProperty, STFAnimationX, HandoffBehavior.SnapshotAndReplace);
                    stf.BeginAnimation(ScaleTransform.ScaleYProperty, STFAnimationY, HandoffBehavior.SnapshotAndReplace);
                }
            }
        }
    }
}